package com.example.practiceopenglfortexture;

import android.annotation.SuppressLint;
import android.graphics.SurfaceTexture;
import android.opengl.GLES11Ext;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;
import android.os.Handler;
import android.os.Message;
import android.util.Log;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

/**
 * name : TRenderer
 * time : 2019.11.15
 */

public class TRenderer implements GLSurfaceView.Renderer {

    // for log
    private static final String tag = "_" + "TRenderer";
//    private Activity mActivity;
    // for surface view
    private int mTextId;
    private TCamera2 mCamera2;
    private SurfaceTexture mSurfaceTexture;

    TRenderer(TCamera2 tCamera2) {
        mProgram = 0;
        mCamera2 = tCamera2;
    }

    @SuppressLint("HandlerLeak")
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case 0 : mCamera2.startPreview(mSurfaceTexture);break;  // 相机的执行环境需要是主线程
                default: break;
            }

        }
    };

    @Override
    public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
        GLES20.glClearColor(0.0f, .0f, 0.0f, 1.0f); // black
        buildProgram();
        buildTextures();
        mSurfaceTexture = new SurfaceTexture(mTextId);

        Message msg = new Message();
        msg.what = 0;
        msg.obj = "start preview";
        mHandler.sendMessage(msg);
    }

    @Override
    public void onSurfaceChanged(GL10 gl10, int i, int i1) {
        GLES20.glViewport(0, 0, i, i1);
    }

    @Override
    public void onDrawFrame(GL10 gl10) {
        synchronized (this) {
            mSurfaceTexture.updateTexImage();
            GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
            drawFrame();
        }
    }

    private void buildProgram() {
        // create buffers
        createBuffers();
        // create program
        createProgram();
        // get handles
        getHandle();
    }

    private void buildTextures() {
        // set rotation matrix
        float angle = 90;
        Matrix.setRotateM(mvpMatrix, 0, angle, 0, 0, -1);
        
        // build texture
        int[] textures = new int[1];
        // 生成一个纹理id，此时对应的纹理还没有指定维度
        // 产生贴图对象id
        GLES20.glGenTextures(1, textures, 0);
        mTextId = textures[0];
        // 将一个纹理id绑定到一个纹理目标，GLES20.GL_TEXTURE_2D，该纹理为二维纹理，确定了纹理维度
        // 绑定贴图对象到贴图目标上
        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTextId);
        // 设置纹理参数，纹理缩小功能设置，临近采样
        GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
        // 设置纹理参数，纹理放大功能设置，线性采样
        GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
        // 设置纹理参数，纹理x方向拉伸功能设置，截取拉伸
        GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
        // 设置纹理参数，纹理y方向拉伸功能设置，截取拉伸
        GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
    }

    private void drawFrame() {
        Log.d(tag, "draw frame ......");
        // use program
        GLES20.glUseProgram(mProgram);  // 设置项目为实际的渲染目标，激活项目
        // set program's params
        // 指定顶点属性-位置属性
        GLES20.glVertexAttribPointer(positionHandle, 2, GLES20.GL_FLOAT, false, 8, rectBuffer); // 顶点坐标设置顶点位置
        GLES20.glEnableVertexAttribArray(positionHandle);
        // 指定顶点属性-贴图坐标
        GLES20.glVertexAttribPointer(textureHandle, 2, GLES20.GL_FLOAT, false, 8, textBuffer);  // 纹理坐标设置纹理位置
        GLES20.glEnableVertexAttribArray(textureHandle);
        // active textures
        // 激活贴图单元
        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);             // 激活纹理单元（GLES20.GL_TEXTURE0）
        // 为当前贴图单元绑定贴图对象
        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTextId); // 将纹理id绑定激活的纹理单元，使得纹理单元与纹理目标对应
        // 为片段着色器中的y采样器指定已经绑定过的贴图单元0
        GLES20.glUniform1i(samplerHandle, 0);                      // 将激活的纹理单元0对应的纹理目标内内的图像数据添加到变量samplerHandle中
        // set rotation matrix
        GLES20.glUniformMatrix4fv(matrixHandle, 1, false, mvpMatrix, 0);
        // get rgb texture
//        GLES20.glGetu
        // draw frame
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
        GLES20.glFinish();
        //
        GLES20.glDisableVertexAttribArray(positionHandle);
        GLES20.glDisableVertexAttribArray(textureHandle);
    }

    // for program
    private int mProgram;
    // for shader codes
    private int positionHandle;
    private int textureHandle;
    private int matrixHandle;
    private int samplerHandle;
    // for rotation matrix
    private float[] mvpMatrix = new float[16];
    // for coordinates
    private ByteBuffer rectBuffer;  // 存储顶点坐标的缓冲区
    private ByteBuffer textBuffer;  // 存储纹理坐标的缓冲区
    // for rectangle vertex coordinate
    private static float[] rectVertices = {
            -1.0f,-1.0f,
            1.0f,-1.0f,
            -1.0f, 1.0f,
            1.0f, 1.0f
    };
    // for texture vertex coordinate
    private static float[] textVertices = {
            0.0f, 1.0f,
            1.0f, 1.0f,
            0.0f, 0.0f,
            1.0f, 0.0f
    };
    // for vertex shader code
    private static final String vertexShaderCode =
                    "attribute vec4 vPosition;\n" +
                    "attribute vec2 textCoord;\n" +
                    "uniform mat4 uMVPMatrix;\n" +
                    "varying vec2 tc;\n" +            // 纹理坐标
                    "void main() {\n" +
                    "   gl_Position = uMVPMatrix * vPosition;\n" +
                    "   tc = textCoord;\n" +
                    "}\n";
    // for fragment shader code
    private static final String fragmentShaderCode =
            "#extension GL_OES_EGL_image_external : require\n" + // 声明对该扩展的使用
            "precision mediump float;\n" +                 // 精度
            "uniform samplerExternalOES s_texture;\n" +    // 纹理采样器
            "varying vec2 tc;\n" +
            "void main() {\n" +
            "   gl_FragColor = texture2D(s_texture, tc);\n" +
            "}\n";

    private void createBuffers() {
        // for rectangle
        /*
        下面ByteBuffer的两个静态函数，都是为ByteBuffer类对象分配缓冲区的
        不同的是，创建direct的缓冲区（操作系统内存），会提升该对象参与IO操作的性能
         */
//        rectBuffer = ByteBuffer.allocate(rectVertices.length * 4);
        rectBuffer = ByteBuffer.allocateDirect(rectVertices.length * 4);
        /*
        下面ByteBuffer的方法是设置ByteBuffer的字节序
        ByteOrder.BIG_ENDIAN    大字节序
        ByteOrder.LITTLE_ENDIAN 小字节序
        ByteOrder.nativeOrder() 返回当前硬件平台的字节序
         */
        rectBuffer.order(ByteOrder.nativeOrder());
        /*
        将ByteBuffer设置为FloatBuffer，并写入数据
         */
        rectBuffer.asFloatBuffer().put(rectVertices);
        /*
        设置接下来需要读写操作的位置
        初始化时读写位置为0，但上面进行了一次写入操作，位置改变，需要重新设置
         */
        rectBuffer.position(0);
        // for textures
        textBuffer = ByteBuffer.allocateDirect(textVertices.length * 4);
        textBuffer.order(ByteOrder.nativeOrder());
        textBuffer.asFloatBuffer().put(textVertices);
        textBuffer.position(0);
    }

    private void createProgram() {
        int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
        int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);

        mProgram = GLES20.glCreateProgram();                // 创建项目句柄
        if (mProgram != 0) {
            GLES20.glAttachShader(mProgram, vertexShader);  // 将着色器添加到项目中
            GLES20.glAttachShader(mProgram, fragmentShader);// 同上
            GLES20.glLinkProgram(mProgram);                 // 链接项目
            int[] linkStatus = new int[1];
            GLES20.glGetProgramiv(mProgram, GLES20.GL_LINK_STATUS, linkStatus, 0);
            if (linkStatus[0] != GLES20.GL_TRUE) {
                Log.e(tag, "link program fail !");
                GLES20.glDeleteProgram(mProgram);
                mProgram = 0;
            }
        } else {
            Log.e(tag, "create program fail !");
        }
    }

    private int loadShader(int shaderType, String shaderCode) {
        int shader = GLES20.glCreateShader(shaderType); // 创建着色器句柄
        if (shader != 0) {
            GLES20.glShaderSource(shader, shaderCode);  // 替换着色器中的源代码
            GLES20.glCompileShader(shader);             // 编译新替换的源代码
            int[] compiled = new int[1];
            GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
            if (compiled[0] != GLES20.GL_TRUE) {
                Log.e(tag, "compile shader fail !");
                Log.e(tag, "shader:" + shaderCode);
                GLES20.glDeleteShader(shader);
                shader = 0;
            }
        } else {
            Log.e(tag, "create shader fail !");
        }
        return shader;
    }

    private void getHandle() {
        positionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
        textureHandle = GLES20.glGetAttribLocation(mProgram, "textCoord");
        matrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
        samplerHandle = GLES20.glGetUniformLocation(mProgram, "text_sampler");
    }
}
